﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

namespace TransportLayer.NodeLibrary
{
    public class SFM : IFrameReceiver
    {
          private Dictionary<RoutingEntry, RoutingEntry> matrix;
          private Dictionary<int, RoutingEntry> map;
          private Node node;
          private Random rand;
          private NodeSNP snp;
          private int Rand { get { return rand.Next(); } }
          
          public SFM(Node node)
          {
              this.node = node;
              this.rand = new Random();
              matrix = new Dictionary<RoutingEntry, RoutingEntry>(new RoutingEntry.REComparer());
              map = new Dictionary<int, RoutingEntry>();
          }

          
        public SFM(SFM sfm)// : base((Dictionary<RoutingEntry, RoutingEntry>)routingTable, new RoutingEntry.EqualityComparer()) { }
        {
            matrix = new Dictionary<RoutingEntry, RoutingEntry>(sfm.matrix, new RoutingEntry.REComparer());
            map = new Dictionary<int, RoutingEntry>(sfm.map);
           
        }

        public void SetSNP(NodeSNP snp)
        {
            this.snp = snp;
        }


        public void ReceiveFrame(ProtocolUnit pu, int port) 
          {
              RoutingEntry key = new RoutingEntry(pu.Stream, port);
              if (matrix.ContainsKey(key))
              {
                      RoutingEntry forward = matrix[key];

                      if (pu.Put == ProtocolUnitType.NORMAL_PACKET && node.PortsOut.Length >= forward.Port && node.PortsOut[forward.Port] != null)
                      {   
                          pu.Stream = forward.Stream;
                          node.PortsOut[forward.Port].Send(pu);
                          node.Log.LogMsg("Sending " + key.Stream + ":" + key.Port + ">" + forward.Stream + ":" + forward.Port +".");
                      }
                      else if (pu.Put == ProtocolUnitType.CONF_PACKET)
                      {
                          string [] param = pu.Message.Split(' ');
                          RoutingEntry re = new RoutingEntry(Int32.Parse(param[0]), port);
                          int connectionID = Int32.Parse(param[1]);
                          if (!map.ContainsKey(connectionID))
                          {
                              map.Add(connectionID, re);
                              snp.Send("zLRM ok");
                          }
                          else { }
                              snp.Send("zLRM fail");

                      }
                      else node.Log.LogMsg("Ramka " +  pu.Stream + ":"+ port +"odrzucona.");

               }
              
          }

          public bool ContainsKey(int connectionID)
          {
              return map.ContainsKey(connectionID);

          }

          public bool ContainsKey(RoutingEntry re)
          {
              return matrix.ContainsKey(re);

          }
        
          public bool ContainsValue(RoutingEntry re)
          {
              return matrix.ContainsValue(re);
          }


          /*
           * to remove evry MatrixLink with this ID
           */ 
          public bool Remove(int connectionID)
          {
              if (map.ContainsKey(connectionID))
              {
                  matrix.Remove(map[connectionID]);
                 // map.Remove(connectionID);
                  return map.Remove(connectionID); ;
              }
              else return false;          
          }
        


          public bool Add(string command,string connectionID)
          {
              try{
              string [] param = command.Split('>');
              int portIn = Int32.Parse(param[1]);
              int portOut = Int32.Parse(param[2]);
              int conID = Int32.Parse(connectionID);
             
                  if (param.Length == 2) //&& map.ContainsKey(conID))
                     {
                         int port = Int32.Parse(param[1]);
                         RoutingEntry entry = map[conID];
                         int stream=Rand;
                         while (map.ContainsKey(stream))
                         { stream = Rand; }
                         RoutingEntry depart = new RoutingEntry(stream, portOut);
                         matrix.Add(entry, depart);

                         node.PortsOut[portOut].Send(this.ConfigProtocolUnit(depart.Stream, conID));
                         snp.Send("aLRM ok");
                      //map.Add(Int32.Parse(connectionID), entry);
                      return true;    
                     }
                  
              }
                catch (FormatException) { return false; }
                catch (ArgumentException) { return false; }
                catch (RoutingEntry.ArgumentsLenghtException) { return false; }
              
              return false;

          }

         private ProtocolUnit ConfigProtocolUnit(int rand, int connectionID)
         {
             ProtocolUnit pu = new ProtocolUnit();
             pu.Put = ProtocolUnitType.CONF_PACKET;
             pu.Message = "" + rand + " " + connectionID;

             return pu;
         }
          public void Add(RoutingEntry e1, RoutingEntry e2)
          {
              matrix.Add(e1, e2);
              map.Add(e1.Stream, e1);
          }
          

          public override string ToString()
          {     string str = "";
                foreach (KeyValuePair<int, RoutingEntry> kvp in map)
                    str += "[" + kvp.Key + "] " + kvp.Value.ToString() + ">" + matrix[kvp.Value].ToString() +"\n";
                return str;
          }
    }

    public class RoutingEntry : System.IEquatable<RoutingEntry>
    {
        private int port;
        public int Port { get { return port; }  }


        //number of stream
        private int stream;
        public int Stream { get { return stream; }  }

        public static IEqualityComparer<RoutingEntry> Default
        { get { return (IEqualityComparer<RoutingEntry>)(new REComparer()); } }

        public RoutingEntry(int stream, int port)
        {
            this.stream = stream;
            this.port = port;
        
        }

        public RoutingEntry(string comand)
        {
            comand = comand.Replace(">", "");
            string[] param = comand.Split(';', ':', ',');
            if (param.Length == 2)
            {
                this.stream = Int32.Parse(param[0]);
                this.port = Int32.Parse(param[1]);
            }
            else throw new ArgumentsLenghtException();
        }

        public class REComparer : IEqualityComparer<RoutingEntry>
        {
            public bool Equals(RoutingEntry e1, RoutingEntry e2)
            {
                if (e1.Stream == e2.Stream && e1.Port == e2.Port)
                    return true;
                else
                    return false;

            }

            public int GetHashCode(RoutingEntry entry)
            {
                return entry.Port ^ entry.Stream;
            }
        }

        public override string ToString()
        {
            return  this.Stream + ":" + this.Port;
        }

        public bool Equals(RoutingEntry other)
        {
            if (this.Stream == other.Stream && this.Port == other.Port)
                return true;
            else
                return false;

        }

        public class ArgumentsLenghtException : Exception
        {
            public int Id = -1;
            public ArgumentsLenghtException()
            { }
            public ArgumentsLenghtException(int id)
            {
                Id = id;
            }
        }

    }

    public class RoutingEntryCollection : IEnumerable, IEnumerator
    {
         private RoutingEntry [] routingEntry;
         private int position = -1;

        public int Count { get { return routingEntry.Length; } }
       
        public RoutingEntryCollection(RoutingEntry[] rArray)
        {
                routingEntry = rArray;
        }

        public RoutingEntryCollection(string comand)
        {
            string[] param = comand.Split(',', ';', ':');

            if(param.Length>1)
            {
                int stream = Int32.Parse(param[0]);
                routingEntry = new RoutingEntry[param.Length - 1];

                for (int i = 1; i < param.Length; i++)
                {
                    routingEntry[i - 1] = new RoutingEntry(stream, Int32.Parse(param[i]));
                }
                    

            } else throw(new Exception("Not enough arg"));
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator)GetEnumerator();
        }

        public RoutingEntryCollection GetEnumerator()
        {
            this.Reset();
            return this;
        }

        public bool MoveNext()
        {
            position++;
            return (position < routingEntry.Length);
        }

        public void Reset()
        {
            position = -1;
        }

        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }

        public RoutingEntry Current
        {
            get
            {
                try
                {
                    return routingEntry[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
        public bool Remove(int stream, int port)
        {
            RoutingEntry matrixlink = new RoutingEntry(stream, port);
            List<RoutingEntry> re = new List<RoutingEntry>(routingEntry);

            bool value = re.Remove(matrixlink);
            routingEntry = re.ToArray();
            return value;
            
        }
        public override string ToString()
        {
            string str = routingEntry[0].Stream.ToString();

            foreach (RoutingEntry re in this)
            {
                str += ":" + re.Port;
            }
            return str;
        }


    }
    //jeszcez nei wiem czy to uzyje
    public class MatrixConnection
    {
        private int streamIn;
        private int streamOut;

        private int portIn;
        private List<int> portOuts;

        public MatrixConnection(string command)
        {
            string[] param = command.Split('>');
            if (param.Length != 2)
                throw new Exception("parm fail");


        
        }

        private void SetIncomming(string command)
        {
            string [] param = command.Split(':');
            if (param.Length != 2)
                throw new Exception("param fail");
            else
            {
                streamIn = Int32.Parse(param[0]);
                portIn = Int32.Parse(param[1]);
            }
        }
        private void SetOutcomming(string command)
        { 
            string [] param = command.Split(':');
            if (param.Length > 1)
                throw new Exception("param fail");
            streamOut = Int32.Parse(param[0]);
            for (int i = 1; i < param.Length; i++)
                this.portOuts.Add(Int32.Parse(param[i]));

             
        
        }
        public override string ToString()
        {
            string tmp = "";
            foreach (int i in portOuts)
                tmp += ":" + i;
            return ""+streamIn+":"+portIn+">" + streamOut + tmp;
        }
    }

    }




